﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Nature.Toolkit
{
    public static class DynamicAssemblyUtils
    {
        private readonly static Dictionary<Guid, Assembly> AssemblyVsSourceChecksum = new Dictionary<Guid, Assembly>();
        private readonly static ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
        

        public static Assembly GetBySourceCodes(CompilerParameters parameters, string[] sources)
        {
            Guid checksum = Numerics.Utilities.CalcMD5Hash(sources.OrderBy(s => s));
            Locker.EnterReadLock();
            try
            {
                Assembly assembly = null;
                if(AssemblyVsSourceChecksum.TryGetValue(checksum, out assembly))
                {
                    return assembly;
                }
            }
            finally
            {
                Locker.ExitReadLock();                
            }
            
            Locker.EnterWriteLock();
            try
            {
                Assembly assembly = null;
                if(AssemblyVsSourceChecksum.TryGetValue(checksum, out assembly))
                {
                    return assembly;
                }
                var codeDomProvider = new Microsoft.CSharp.CSharpCodeProvider();
                var compilerResults = codeDomProvider.CompileAssemblyFromSource(parameters, sources);
                if (true == compilerResults.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (CompilerError err in compilerResults.Errors)
                    {
                        string errorMessage = string.Format("{0} {1} Line: {2} {3}", err.ErrorNumber, err.FileName, err.Line, err.ErrorText);
                        sb.AppendLine(errorMessage);
                        Trace.WriteLine(errorMessage);
                    }

                    throw new System.InvalidOperationException(sb.ToString());
                }
                AssemblyVsSourceChecksum.Add(checksum, compilerResults.CompiledAssembly);
                return compilerResults.CompiledAssembly;
            }
            finally
            {
                Locker.ExitWriteLock();
            }
        }
    }
}
